#!/bin/sh
# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

logger -- "$0" "$@"

. /usr/share/misc/shflags
. $(dirname "$0")/xinput.sh

# Sets mouse sensitivity preference
apply_mouse_sensitivity() {
  device_set_prop "$1" "Pointer Sensitivity" "$2"
  device_set_prop "$1" "Scroll Sensitivity" "$2"
}

# Sets touchpad sensitivity preference
apply_touchpad_sensitivity() {
  device_set_prop "$1" "Pointer Sensitivity" "$2"
  device_set_prop "$1" "Scroll Sensitivity" "$2"
}

apply_mouse_swap_lr() {
  local map="1 2 3"
  if [ "$2" -eq 1 ]; then
    map="3 2 1"
  fi
  log "$XINPUT get-button-map $1 | sed \"s/^[0-9]* [0-9]* [0-9]*/$map/\" | \
      xargs $XINPUT set-button-map $1"
  $XINPUT get-button-map "$1" | sed "s/^[0-9]* [0-9]* [0-9]*/$map/" | \
      xargs $XINPUT set-button-map "$1"
}

apply_tp_custom_point_accel() {
  the_id="$1"
  values="$(echo "$2" | sed 's/,/ /g')"
  $XINPUT set-prop "$the_id" "Pointer Accel Curve" \
      $values
}

apply_tp_custom_scroll_accel() {
  the_id="$1"
  values="$(echo "$2" | sed 's/,/ /g')"
  $XINPUT set-prop "$the_id" "Scroll Accel Curve" \
      $values
}

apply_mouse_custom_point_accel() {
  the_id="$1"
  values="$(echo "$2" | sed 's/,/ /g')"
  $XINPUT set-prop "$the_id" "Mouse Pointer Accel Curve" \
      $values
}

# Generate activity log files for all devices
generate_logs() {
  local devices="$1"
  local now=$(date '+%Y%m%d-%H%M%S')
  local raw_file="/var/log/xorg/touchpad_activity_log.txt"
  local cmt_log_file="/var/log/xorg/cmt_input_events.dat"
  local evdev_log_file="/var/log/xorg/evdev_input_events.dat"

  local out_dir="$2"
  local out_prefix="touchpad_activity_"
  local cmt_out_prefix="cmt_input_events_"
  local evdev_out_prefix="evdev_input_events_"

  # Dump log files for each device
  for device in $devices ; do
    local name=$(device_get_canonical_name $device)
    local out_file="${out_dir}/${out_prefix}${now}.$device.$name"
    local cmt_out_file="${out_dir}/${cmt_out_prefix}${now}.$device.$name"
    local evdev_out_file="${out_dir}/${evdev_out_prefix}${now}.$device.$name"

    if ! device_is_touchscreen $device; then
      device_set_prop $device "Logging Notify" 1
    fi
    device_set_prop $device "Dump Debug Log" 1

    # Wait for writing to the files to complete. We do this by waiting for
    # two consecutive checks in which the file size of the logs doesn't
    # change.
    local raw_size="-1"
    local events_size="-1"
    local max_iterations=6
    while true; do
      if device_is_touchscreen $device; then
        local raw_size_new=0
        local events_size_new=$(stat -c %s $evdev_log_file)
      else
        local raw_size_new=$(stat -c %s $raw_file)
        local events_size_new=$(stat -c %s $cmt_log_file)
      fi

      if [ $raw_size -eq $raw_size_new -a \
           $events_size -eq $events_size_new ]; then
        break
      fi
      max_iterations=$((max_iterations - 1))
      if [ $max_iterations -lt 0 ]; then
        break
      fi
      sleep 0.5
      raw_size=$raw_size_new
      events_size=$events_size_new
    done

    if [ -d $out_dir ]; then
      if device_is_touchscreen $device ; then
        cp "$evdev_log_file" "$evdev_out_file"
      else
        gzip -c --stdout "$raw_file" > "$out_file"
        gzip -c --stdout "$cmt_log_file" > "$cmt_out_file"
      fi
    else
      echo "\"$out_dir\" is not a directory."
    fi
  done

  # Delete old log files
  if [ -d $out_dir ]; then
    del="$(ls -t ${out_dir}/${out_prefix}* | grep -v ${now})"
    del="${del} $(ls -t ${out_dir}/${cmt_out_prefix}* | grep -v ${now})"
    del="${del} $(ls -t ${out_dir}/${evdev_out_prefix}* | grep -v ${now})"
    for file in $del ; do
      rm -f "$file"
    done
  fi
}

# Mouse specific device setup
setup_mouse() {
  local device="$1"
  device_set_prop $device "Device Accel Profile" -1
  device_set_prop $device "Device Accel Constant Deceleration" 1
  device_set_prop $device "Device Accel Velocity Scaling" 1
}

# Detup device preferences and properties
setup_device() {
  local device=$1
  apply_all_preferences $device

  if device_is_mouse $device; then
    setup_mouse $device
  fi
}

# Helper method to add flags that set user preferences
DEFINE_prop_preference() {
  local name="$2"
  local prop_name="$5"
  eval "apply_${name} () { device_set_prop \"\$1\" \"${prop_name}\" \"\$2\"; }"
  DEFINE_preference "$1" "$2" "$3" "$4"
}

DEFINE_preference() {
  local name=$2

  DEFINE_string ${name} '' "$3 set ${name} preference"
  eval "pref_${name}_type=\"$1\""
  eval "pref_${name}_validator=\"$3\""
  eval "pref_${name}_default=\"$4\""
  preferences="$preferences $name"
}

DEFINE_string 'type' 'all' 'type of devices to operate on' 't'
DEFINE_string 'id' '' 'id to operate on' 'i'
DEFINE_boolean 'verbose' false \
      'show xinput commands executed by this script' 'v'
DEFINE_boolean 'list' false 'list device xinput ids'
DEFINE_boolean 'hwprops' false 'print hardware properties of device'
DEFINE_boolean 'names' false 'list device ids and their names'
DEFINE_boolean 'status' false 'show devices status'
DEFINE_string 'add' '' 'to be called by udev when a new device is added'
DEFINE_boolean 'refresh' false 're-apply preferences to all selected devices'

DEFINE_boolean 'log' false 'generate activityt log files'
DEFINE_string 'logdir' '/home/chronos/user/log' \
      'use with --log. Choose target directory'

DEFINE_prop_preference touchpad tapdrag "[01]" "0" "Tap Drag Enable"
DEFINE_prop_preference touchpad tapclick "[01]" "1" "Tap Enable"
DEFINE_prop_preference touchpad t5r2_three_finger_click "[01]" "0" \
      "T5R2 Three Finger Click Enable"
DEFINE_prop_preference multitouch australian_scrolling "[01]" "1" \
      "Australian Scrolling"
DEFINE_prop_preference nontouch_mouse mouse_australian_scrolling "[01]" "0" \
      "Australian Scrolling"
DEFINE_preference touchpad touchpad_sensitivity "[12345]" "3"
DEFINE_preference mouse mouse_sensitivity "[12345]" "3"
DEFINE_preference mouse mouse_swap_lr "[01]" "0"
DEFINE_prop_preference mouse mouse_old_accel "[01]" "0" \
      "Old Mouse Accel Curves"

default_curve_string() {
  # Trick to print this string (up to % sign) 20 times
  printf "inf 0 1 0 %.0s" {1..20}
}

DEFINE_preference touchpad tp_custom_point_accel "*" \
      "$(default_curve_string)"
DEFINE_preference multitouch tp_custom_scroll_accel "*" \
      "$(default_curve_string)"
DEFINE_preference mouse mouse_custom_point_accel "*" \
      "$(default_curve_string)"

DEFINE_prop_preference touchpad use_tp_custom_point_accel "[01]" "0" \
      "Use Custom Touchpad Pointer Accel Curve"
DEFINE_prop_preference multitouch use_tp_custom_scroll_accel "[01]" "0" \
      "Use Custom Touchpad Scroll Accel Curve"
DEFINE_prop_preference mouse use_mouse_custom_point_accel "[01]" "0" \
      "Use Custom Mouse Pointer Accel Curve"

main() {
  if ! [ -z ${FLAGS_add} ] ; then
    local device=$(wait_and_get_added_device ${FLAGS_add})
    setup_device $device
    return
  fi

  # Determine list of devices to use for commands
  local devices=""
  if ! [ -z "${FLAGS_id}" ] ; then
    devices=${FLAGS_id}
  else
    case ${FLAGS_type} in
      mouse) devices=$(list_mice);;
      touchpad) devices=$(list_touchpads);;
      touchscreen) devices=$(list_touchscreens);;
      multitouch) devices=$(list_multitouch);;
      multitouchmouse) devices=$(list_multitouch_mice);;
      all) devices=$(list_devices);;
      *)
        echo "error: unknown device type ${FLAGS_type}" >&2
        flags_help
      ;;
    esac
  fi

  # Process preferences that have been set in arguments
  for pref in $preferences; do
    flag="$(eval echo \${FLAGS_$pref})"
    if ! [ -z "$flag" ]; then
      if validate_preference "$pref" "$flag"; then
        apply_preference "$pref" "$devices" "$flag"
        save_preference "$pref" "$flag"
      fi
    fi
  done

  # Refresh other properties of touchpads/mice.
  # We do this here because Chrome doesn't call --refresh.
  # We abuse chrome's calls to set sensitivity.
  if ! [ -z ${FLAGS_mouse_sensitivity} ] ; then
    devs="$(list_mice)"
    for dev in $devs; do
      setup_device $dev
    done
  fi
  if ! [ -z ${FLAGS_touchpad_sensitivity} ] ; then
    devs="$(list_touchpads)"
    for dev in $devs; do
      setup_device $dev
    done
  fi

  # Process commands
  if [ ${FLAGS_list} -eq ${FLAGS_TRUE} ] ; then
    if ! [ -z $devices ]; then
      echo $devices
    fi
  fi
  if [ ${FLAGS_hwprops} -eq ${FLAGS_TRUE} ] ; then
    for device in $devices; do
      local path=$(device_get_string_prop "$device" "Device Node")
      log "evemu-describe $path"
      evemu-describe $path
      if [ $? -ne 0 ]; then
        exit 1
      fi
    done
  fi
  if [ ${FLAGS_status} -eq ${FLAGS_TRUE} ] ; then
    for device in $devices; do
      device_status $device
    done
  fi
  if [ ${FLAGS_names} -eq ${FLAGS_TRUE} ] ; then
    for device in $devices; do
      echo "${device}: $(device_get_name $device)"
    done
  fi
  if [ ${FLAGS_log} -eq ${FLAGS_TRUE} ] ; then
    generate_logs "$devices" "${FLAGS_logdir}"
  fi
  if [ ${FLAGS_refresh} -eq ${FLAGS_TRUE} ] ; then
    for device in $devices; do
      setup_device $device
    done
  fi

  # Process "Property Name=Value" arguments
  for arg; do
    if echo $arg | grep -q =; then
      local name="${arg%%=*}"
      local value="${arg#*=}"
      for device in $devices; do
        device_set_prop "$device" "$name" "$value"
      done
    else
      flags_help
    fi
  done
}

if [ $# = 0 ] ; then
  flags_help
  exit 1
fi

# parse the command-line
FLAGS "$@" || exit $?
eval set -- "${FLAGS_ARGV}"
main "$@"
